﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace AsyncServer
{
    class Contexto
    {
        public NetworkStream stream  { get; private set; }
        public byte[]        buffer  { get; private set; }
        public TcpClient     cliente { get; private set; }

        public Contexto(TcpClient cliente, NetworkStream stream, byte[] buffer)
        {
            this.cliente = cliente;
            this.stream = stream;
            this.buffer = buffer;
        }
    }

    class Program
    {
        const int MAX_BUFF_READ = 1024;
        static ManualResetEvent evClienteAceptado = new ManualResetEvent(false);

        static void Main(string[] args)
        {
            Console.WriteLine("main");
            try
            {
                TcpListener listener = new TcpListener(IPAddress.Any, 1337);
                listener.Start(5);

                Console.WriteLine("servidor escuchando");

                for (; ; )
                {
                    evClienteAceptado.Reset();
                    listener.BeginAcceptTcpClient(cbAcceptTcpClient, listener);
                    evClienteAceptado.WaitOne();
                }
            }
            catch (Exception e)
            {
                int foo = 0;
            }
        }

        static void cbAcceptTcpClient(IAsyncResult iar)
        {
            Console.WriteLine("accept");
            try
            {
                TcpListener listener = (TcpListener)iar.AsyncState;

                TcpClient cliente = listener.EndAcceptTcpClient(iar);

                evClienteAceptado.Set();
                Console.WriteLine("cliente aceptado: {0}", cliente.GetHashCode());
                NetworkStream stream = cliente.GetStream();
                byte[] buffer = new byte[MAX_BUFF_READ];
                stream.BeginRead(
                    buffer, 
                    0, 
                    buffer.Length, 
                    cbClienteRead, 
                    new Contexto(cliente, stream, buffer)
                );

            }
            catch (Exception e)
            {
                int foo = 0;
            }
        }

        static void cbClienteRead(IAsyncResult iar)
        {
            Console.WriteLine("read");
            Contexto contexto = null;
            try
            {
                contexto = (Contexto)iar.AsyncState;

                int leidos = contexto.stream.EndRead(iar);

                if (leidos == 0)
                {
                    Console.WriteLine("cerrando cliente {0}", contexto.cliente.GetHashCode());
                    contexto.cliente.Close();
                    contexto = null;
                    return;
                }

                // para debug
                //Console.WriteLine(leidos > 0 ? "SI" : "NO");

                // muestro lo que leí
                Console.WriteLine(
                    "cliente {0} dice: {1}", 
                    contexto.cliente.GetHashCode(), 
                    Encoding.ASCII.GetString(contexto.buffer, 0, leidos)
                );

                // borro el buffer
                contexto.buffer.Initialize();

                // vuelvo a leer
                contexto.stream.BeginRead(
                    contexto.buffer,
                    0,
                    contexto.buffer.Length,
                    cbClienteRead,
                    contexto
                );
            }
            catch (Exception e)
            {
                if (contexto != null)
                {
                    Console.WriteLine("cerrando cliente {0} (a causa de error)", contexto.cliente.GetHashCode());
                    contexto.cliente.Close();
                    contexto = null;
                    return;
                }
            }
        }
    }
}
